#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <stdbool.h>
#include <stdio.h>
#include <sstream>
#include <fstream>
#include <sys/time.h>
#include <errno.h>
#include <termios.h>
using namespace std;


#define MAXJOBS 10   /* max jobs at any point in time */

#define TRUE 1
#define FALSE !TRUE

#define UNDEF 0	/* undefined state */
#define FG 1    /* running in foreground */
#define BG 2    /* running in background */
#define SU 3    /* suspend */
#define WI 4	/* waits for input*/


vector<string>PATH;					// locations of paths for commands
vector<string>DATA;					// location of data being stored
vector<string> store;				// stores the commands and arguments
queue<string> processQ;
vector<int> exit_status;				// stores the execution statuses of commands
vector<string>configf;
vector<string>forloop;				// stores the execution statuses of commands


typedef struct job{		/* The job struct */
	int jid;		/* job ID [1, 2, ...] */
	string cmd;		/* command line */
	pid_t pid;		/* job PID */
	pid_t pgid;		/* process group ID */
	int state;		/* UNDEF, BG, FG, or ST */
} t_job;

t_job jobslist[MAXJOBS];

int numJobs = 0;
int nextjid = 0;

int CM_TERMINAL,CM_IS_INTERACTIVE;
struct termios CM_TMODES;
pid_t CM_PID;
pid_t CM_PGID;


int timemax;
int mblimit=1000000;
int mbtimer=100;
int cpulimit=50;
int cputimer=0;



struct timeval startTime;
struct timeval endTime;
struct timeval cpustart;
struct timeval cpuend;
int pCount;
string userPath;

int exec();
int for_exec();
void init();
void Exit_Status();
int launchjob(string cmd, int state_);
void putFG(t_job * job, int continueJob);
void putBG(t_job* job, int continueJob);
void waitJob(t_job* job);
void killJob(int JobID);
void signalHandler_child(int p);
void clearjob( t_job *job);
void initjobs( t_job *jobs);
void addjob( t_job *jobs, pid_t pid, pid_t pgid, int state, string cmdline);
int changeJobStatus( t_job *jobs, int pid, int state);
int deletejob( t_job *jobs, pid_t pid) ;
pid_t fgpid( t_job *jobs);
 t_job *getjobpid( t_job *jobs, pid_t pid);
 t_job *getjobjid( t_job *jobs, int jid);
void printjobs( t_job *jobs);
void catch_interrupt(int sig_num);
void catch_stop(int sig_num);


void clearscreen(){
	char** argv = NULL;
	char** envp = NULL;
	if(fork()==0)
	{
		execve((char*)"/usr/bin/clear", argv, envp);					// clears the screen on start
		exit(1);
	}
	else{
		wait(NULL);
	}
}

/*******See if file exists function*******/
bool FileExists(string strFilename) {
  struct stat stFileInfo;
  bool blnReturn;
  int intStat;

  // Attempt to get the file attributes
  intStat = stat(strFilename.c_str(),&stFileInfo);
  if(intStat == 0) {
    // We were able to get the file attributes
    // so the file obviously exists.
    blnReturn = true;
  } else {
    // We were not able to get the file attributes.
    // This may mean that we don't have permission to
    // access the folder which contains this file. If you
    // need to do that level of checking, lookup the
    // return values of stat which will give you
    // more details on why stat failed.
    blnReturn = false;
  }


  return(blnReturn);
}
/******************************************/



/*********Config File*********************/

void config(string filename)
{
	string filext = ".142";
	size_t found;
	found=filename.find(filext);
	if(found!=string::npos)
	{
		for(int i=0; i<DATA.size(); i++)
		{
			string temp = DATA[i];
			string fullpath = temp.append(filename);

			if(FileExists(fullpath) == true)
			{
				char bug[100];
				string hmm;
				FILE *fp;
				fp = fopen(&fullpath[0], "r");
				if(fp == NULL)
				{
					perror("Error opening file");
				}
				else{
					while(fgets(bug, sizeof(bug)-1 , fp))
					{
						hmm.clear();
						hmm.append(bug);
						configf.push_back(hmm);
						cout<<endl<<hmm;
					}

				}
			}
		}
	}
	else
	{
		cout<<endl<<"File is NOT a shell configuration file"<<endl;
	}
}
/***************************************************/

/***********PARSER************************/
int parse(string in, vector<string> &command, vector<string> &path, vector<string> &data, int &timemax, int &mblimit, int &mbtimer, int &cpulimit, int &cputimer)
{
	int ci = 0;
	int csi = 0;
	ci=in.find_first_of("=");
        if(in.substr(0,ci).compare("PATH")==0)
	{

                bool isDup=false;
		ci++;
		string temp = in.substr(ci);
		csi = temp.find_first_of(":");
		while(csi!=string::npos)
		{

                        for(int i=0; i<path.size(); i++)
                        {
                            if(path[i].compare(temp.substr(0,csi))==0) {
                                isDup = true;
                                break;
                            }
                        }
                    if(!isDup)
			path.push_back(temp.substr(0,csi));
			temp.erase(0,csi+1);
			csi = temp.find_first_of(":");
                        isDup = false;
		}
                for(int i=0; i<path.size(); i++)
                {
                    if(path[i].compare(temp)==0) {
                        isDup = true;
                        break;
                    }
                }
                if(!isDup)
                    path.push_back(temp);
                return 1;
        }else if(in.substr(0,ci).compare("MYPATH")==0)
	{
            ci++;
            userPath = in.substr(ci);
            return 1;
	}else if(in.substr(0,ci).compare("DATA")==0)
	{

                bool isDup=false;
		ci++;
		string temp = in.substr(ci);
		csi = temp.find_first_of(":");
		while(csi!=string::npos)
		{

                        for(int i=0; i<data.size(); i++)
                        {
                            if(data[i].compare(temp.substr(0,csi))==0) {
                                isDup = true;
                                break;
                            }
                        }
                    if(!isDup)
			data.push_back(temp.substr(0,csi));
			temp.erase(0,csi+1);
			csi = temp.find_first_of(":");
                        isDup = false;
		}
                for(int i=0; i<data.size(); i++)
                {
                    if(data[i].compare(temp)==0) {
                        isDup = true;
                        break;
                    }
                }
                if(!isDup)
		data.push_back(temp);
                return 1;
	}
        else if(in.substr(0,ci).compare("MEMMAX")==0)
	{
		ci++;
		string temps2 = in.substr(ci);
		//cout<<temps2;
		csi = temps2.find_first_of(":");
		size_t size = temps2.size();
		string temps3 = temps2.substr(0, csi);
		mblimit = atoi(temps3.c_str())*1024;
		string temps4 = temps2.substr(csi+1, size);
		mbtimer = atoi(temps4.c_str());
	}

	else if(in.substr(0,ci).compare("CPUMAX")==0)
	{
		ci++;
		string temps2 = in.substr(ci);
		csi = temps2.find_first_of(":");
		size_t size = temps2.size();
		string temps3 = temps2.substr(0, csi);
		cpulimit = atoi(temps3.c_str());
		string temps4 = temps2.substr(csi+1, size);
		cputimer = atoi(temps4.c_str());
	}

	else if(in.substr(0,ci).compare("TIMEMAX")==0)
	{
		ci++;
		string temps = in.substr(ci);
		timemax = atoi(temps.c_str());
		return 1;
	}

	if(in.substr(0,ci).compare("bg") == 0){
               command.push_back(in.substr(0,ci+2));
               command.push_back(in.substr(ci+3));
        }
        if(in.substr(0,ci).compare("fg") == 0){
               command.push_back(in.substr(0,ci+2));
               command.push_back(in.substr(ci+3));
        }
        if(in.substr(0,ci).compare("kill") == 0){
               command.push_back(in.substr(0,ci+4));
               command.push_back(in.substr(ci+5));
        }


	ci=in.find_first_of("&");
	if(in[ci+1]!='&'&&(ci==in.size()-1))//background
                    {
			string s = "bg ";
			in=s.append(in.substr(0,ci));
			cout<<in;
                        //command.push_back(in.substr(0,ci+1));
                       // parse(in.substr(ci+1),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                    }
        ci=in.find_first_of("!| &^+()$><#");
        if(ci!=string::npos)
        {
            switch(in[ci])
            {
                case ' ':
                    if(in[ci+1]=='2'&&in[ci+2]=='>')//error redirection
                    {
                        command.push_back(in.substr(0,ci));
                        command.push_back(in.substr(ci+1,2));
                        parse(in.substr(ci+3),command,path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        break;
                    }
                    if(in.substr(0,ci).compare("Cookie")==0)
                        {
                                ci++;
                                string temp = in.substr(ci);
                                config(temp);
                                int m = configf.size();
                                for(int i=0; i<configf.size(); i++)
                                {
                                    string s;
                                    if(i<2)
                                    {
                                    s = configf[i].substr(0,configf[i].find_first_of('\n') );
                                    }
                                    else s = configf[i];
                                        parse(s,store, path, data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                                }
                                configf.clear();
                                return 1;
                        }
                    else{
                    command.push_back(in.substr(0,ci));
                    parse(in.substr(ci+1),command,path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                    break;
                    }
                case '$':
                    if(in[ci+1]=='?')
                    {
                    command.push_back(in.substr(0,ci+2)); //$?
                    command.push_back(in.substr(ci+2)); // stored exit index
                    break;
                    }
                    else if(in[ci+1]=='M')
                    {
                        string s = userPath;
                        parse(s.append(in.substr(ci+8)),command,path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        break;
                    }
                case '&':
                    //logical and
                        pCount++;
                        command.push_back(in.substr(0,ci));
                        command.push_back(in.substr(ci,2));
                        parse(in.substr(ci+2),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        break;
                case '|':
                    if(in[ci+1]!='|')//pipe
                    {   pCount++;
                        command.push_back(in.substr(0,ci));
                        command.push_back(in.substr(ci,1));
                        parse(in.substr(ci+1),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        break;
                    }
                    else{//logical or
                        pCount++;
                        command.push_back(in.substr(0,ci));
                        command.push_back(in.substr(ci,2));
                        parse(in.substr(ci+2),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        break;
                    }
                case '>':
                        command.push_back(in.substr(0,ci));
                        command.push_back(in.substr(ci,1));
                        parse(in.substr(ci+1),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        pCount++;
                        break;
                case '<':
                        command.push_back(in.substr(0,ci));
                        command.push_back(in.substr(ci,1));
                        parse(in.substr(ci+1),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        pCount++;
                        break;
                case '#':
                    if(in[ci+1]=='D')
                    {
                        bool exist = false;
                        int c = in.substr(ci+3).find_first_of("| &^+()$><#");
                        for(int i=0; i<DATA.size(); i++)
                        {
                            if(c==0)
                            {
                                string s = in.substr(ci+3,1);
                                command.push_back(DATA[i]);
                                command.push_back(s);
                                exist = true;
                                break;
                            }
                            else
                            {
                                string tempP = DATA[i];
                                string fullpath = tempP.append(in.substr(ci+3,c));
                                if(FileExists(fullpath) == true)
                                {
                                    command.push_back(fullpath);
                                    exist = true;
                                    break;
                                }
                        }
                        }
                        if(!exist)
                        {
                            cout<<"File does not exist"<<endl;
                            return -1;
                        }
                        if(c!=string::npos)
                            parse(in.substr(ci+c+1),command, path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                    }
                    break;
                case '(':
                     if(in.substr(0,ci).compare("for")==0)
                   {
                    int find = in.find("forend");
			string myline;
			forloop.push_back(in);
			while(find == string::npos)
			{

				getline(cin,myline);
				find = myline.find("forend");
				forloop.push_back(myline);

			}

			for_exec();
			break;
                     }
                case'!':
                    pCount++;
                    command.push_back(in.substr(ci,1));
                    parse(in.substr(ci+1),command,path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                    break;
                default:// for all other operator, push on vector,

                        command.push_back(in.substr(ci,1));
                        parse(in.substr(ci+1),command,path,data,timemax,mblimit,mbtimer,cpulimit,cputimer);
                        break;
            }
            return 1;
        }
        else {
            command.push_back(in);
            return 1;
        }
}
/***********HELPER: PROCESSQ_RESTORE******/
void processQ_rest(int pC)
{
    for(int i=0; i<store.size(); i++)
    {
        processQ.push(store[i]);
    }

    pCount = pC;
}
/*****************************************/

/**************Get the difference between two high performance time values*************/
int timediff()
{
    double tS = startTime.tv_sec*1000000 + (startTime.tv_usec);
    double tE = endTime.tv_sec*1000000  + (endTime.tv_usec);
	return (tE-tS)/1000000;
}
/****************************************************************************************/

/**************Get the difference between two high performance time values*************/
int timediff2()
{
	double tS = cpustart.tv_sec*1000000 + (cpustart.tv_usec);
    double tE = cpuend.tv_sec*1000000  + (cpuend.tv_usec);
	return (tE-tS)/1000000;
}
/**************************************************************************************/

/*********************Calculate CPU Percentage of Process *******************************/
int cpuper(pid_t pid)
{
	FILE *fp;
	stringstream o;
	vector <string> cpuapp;
	o << pid;
	string cpid = o.str();
	string utimes, stimes;
	int utime, stime;
	string procfile;

	char buf [200];
	string test1 = "cat /proc/";
	test1.append(cpid);
	test1.append("/stat");
	fp = popen(&test1[0], "r");
	while(fgets(buf, sizeof(buf)-1, fp))
	{
		procfile.append(buf);
	}
	string imabuffer2;
	stringstream ss2(procfile);
	while(ss2 >> imabuffer2)
		{
		cpuapp.push_back(imabuffer2);

		}
	int total2;

		int temp2 = atoi(cpuapp[13].c_str());
		//cout<<endl<<temp2;
		int temp3 = atoi(cpuapp[14].c_str());
		//cout<<temp3;
		int total = temp2+temp3;
		wait(NULL);
	return total;
}
/*****************************************************************************************/

/***********************Is operator*************************/
bool isoper(char oper)
{
	if(oper == '<' ||oper ==  '-' || oper == '>' || oper == '+' || oper == '-' || oper == '/' || oper == '=')
	{
		return true;
	}
	else{
		return false;
	}
}

/**************************************FOR LOOP EXECUTION*********************************/
int for_exec()
{
	int varv;		//x=0;   What x is initialized to
	string oper;		//x<5    oper = "<"
	string cnum;
	int cnumber;		//x<5  cnumber = 5
	string oper2; 		//x+=1   oper2 = how much incrementation per loop


	string forstr = forloop[0];
	size_t find = forstr.find_first_of("=");
	size_t find2 = forstr.find_first_of(";");
	string initial = forstr.substr(find+1,find2-6);
	varv = atoi(initial.c_str());
	find = forstr.find_last_of(";");
	find2 = forstr.find_first_of(";");
	string cond = forstr.substr(find2+2, find-9);


	if((isoper(cond[0]) == true) && (isoper(cond[1]) == true))
	{

		oper = cond.substr(0, 2);
		cnum = cond.substr(2, cond.size());

}
	else{
		oper = cond.substr(0,1);
		cnum = cond.substr(1, cond.size());
	}
	int find3 = forstr.find(")");
	string inc = forstr.substr(find+2, find3-find-2);
	oper2 = inc.substr(0,2);
	string count = inc.substr(2, inc.size());
	int thecount = atoi(count.c_str());
	cnumber = atoi(cnum.c_str());




	if(oper == "<")
	{
		if(oper2 == "+=")
		{
			while(varv < cnumber)
			{

				for(int x=1; x<forloop.size()-1; x++)
				{
					 parse(forloop[x], store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
					 if(!store.empty())										// for commands, not configs
                                         {
                                             processQ_rest(pCount+1);
                                            bool b = processQ.empty();
                                             int a = pCount;

                                             int i = exec();

                                              i = (i <= 0)? 0:1;
                                              exit_status.push_back(i);
                                         }
                                         store.clear();
				}
				varv = thecount + varv;
			}
		}
	}
	if(oper == "<=")
	{
		if(oper2 == "+=")
		{
			while(varv <= cnumber)
			{

				for(int x=1; x<forloop.size()-1; x++)
				{
					 parse(forloop[x], store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
					 if(!store.empty())										// for commands, not configs
                                         {
                                             processQ_rest(pCount+1);
                                            bool b = processQ.empty();
                                             int a = pCount;

                                             int i = exec();

                                              i = (i <= 0)? 0:1;
                                              exit_status.push_back(i);
                                         }
                                         store.clear();
				}
				varv = thecount + varv;
			}
		}
	}
	if(oper == ">")
	{
		if(oper2 == "-=")
		{
			while(varv > cnumber)
			{

				for(int x=1; x<forloop.size()-1; x++)
				{
					 parse(forloop[x], store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
					 if(!store.empty())										// for commands, not configs
                                         {
                                             processQ_rest(pCount+1);
                                            bool b = processQ.empty();
                                             int a = pCount;

                                             int i = exec();

                                              i = (i <= 0)? 0:1;
                                              exit_status.push_back(i);
                                         }
                                         store.clear();
				}
				varv = varv - thecount;
			}
		}
	}
	if(oper == ">=")
	{
		if(oper2 == "-=")
		{
			while(varv >= cnumber)
			{

				for(int x=1; x<forloop.size()-1; x++)
				{
					 parse(forloop[x], store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
					 if(!store.empty())										// for commands, not configs
                                         {
                                             processQ_rest(pCount+1);
                                            bool b = processQ.empty();
                                             int a = pCount;

                                             int i = exec();

                                              i = (i <= 0)? 0:1;
                                              exit_status.push_back(i);
                                         }
                                         store.clear();
				}
				varv = varv - thecount;
			}
		}
	}
	if(oper == "!=")
	{
		if(oper2 == "-=")
		{
			while(varv != cnumber)
			{

				for(int x=1; x<forloop.size()-1; x++)
				{
					 parse(forloop[x], store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
					 if(!store.empty())										// for commands, not configs
                                         {
                                             processQ_rest(pCount+1);
                                            bool b = processQ.empty();
                                             int a = pCount;

                                             int i = exec();

                                              i = (i <= 0)? 0:1;
                                              exit_status.push_back(i);
                                         }
                                         store.clear();
				}
				varv = varv - thecount;
			}
		}
		if(oper2 == "+=")
		{
			while(varv != cnumber)
			{

				for(int x=1; x<forloop.size()-1; x++)
				{
					 parse(forloop[x], store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
					 if(!store.empty())										// for commands, not configs
                                         {
                                             processQ_rest(pCount+1);
                                            bool b = processQ.empty();
                                             int a = pCount;

                                             int i = exec();

                                              i = (i <= 0)? 0:1;
                                              exit_status.push_back(i);
                                         }
                                         store.clear();
				}
				varv = varv + thecount;
			}
		}
	}



forloop.clear();
return 1;





}

/***********EXECUTING_FUNTION*************/
int exec()
{
        int pC = pCount;
        int newPipe_fd[2];
        int oldPipe_fd[2];
        pid_t pid;
        //create argument array of character pointers
        string fileLoc;

        //exit status
	int exitStat;


                if(processQ.empty())
                    processQ_rest(pC);
                string tempCMD;


                for(int i = 0; i < pCount; i++) {

                        int index;
                        char** arguments = new char* [processQ.size()+1];
                        string op;
                        if(!processQ.empty()&&(processQ.front().compare("!")==0))
                        {
                            op = processQ.front();
                            processQ.pop();
                            pCount--;
                        }
                        if(!processQ.empty()&&(processQ.front().compare("||")==0||processQ.front().compare("&&")==0
                                        ))
                        {
                            op = processQ.front();
                            processQ.pop();
                            if(op.compare("&&")==0&&exitStat>0)
                            {
                                pCount=0;
                                break;
                            }
                            else if(op.compare("||")==0&&exitStat<=0)
                            {
                                pCount=0;
                                break;
                            }
                        }
                        tempCMD = processQ.front();
                        for(index = 0; !processQ.empty(); index++)
                        {       char* w;
                                if(processQ.empty()||processQ.front()=="|"||processQ.front()==">"||processQ.front()=="<"
                                        ||processQ.front().compare("||")==0||processQ.front().compare("&&")==0
                                        ||processQ.front().compare("!")==0||processQ.front().compare("2>")==0) break;
                                arguments[index] = const_cast<char*> (processQ.front().c_str());
                                w=arguments[index];
                                processQ.pop();
                        }
                        //appending null at the end
                        arguments[index] = '\0';
                        if(!processQ.empty()&&processQ.front()=="|")
                         processQ.pop();

                        if(!processQ.empty()&&(processQ.front()==">"||processQ.front()=="<"||processQ.front().compare("2>")==0))
                        {
                            op=processQ.front();
                            processQ.pop();
                            fileLoc = processQ.front();
                            processQ.pop();
                        }
                        const char* cmd;
                        for(int x = 0; x < PATH.size(); x++)
                        {
                            //temp string to store path, avoid altering PATH after each append
                            string temp = PATH[x];
                            temp.append(tempCMD);
                            if(FileExists(temp))
                            {
                                cmd = (temp).c_str();
                                break;
                            }
                        }

                        //fork and execute
                       if(i < pCount-1) {
                                pipe(newPipe_fd);
                        }

                        pid = fork();
                        //STDIN_FILE0 0
                        //STDOUT_FILE0 1
                        //STDERR_FILE0 2
                        if(pid == 0) {
                                alarm(timemax);
                                if(!processQ.empty()&&(processQ.front().compare("||")==0||processQ.front().compare("&&")==0
                                        ))
                                {   exitStat = execvp(cmd, arguments);
                                _exit(0);                        }

                                // do we have a previous command?
                                if(i > 0) {
                                        close(oldPipe_fd[1]);
                                        dup2(oldPipe_fd[0], 0);
                                        close(oldPipe_fd[0]);
                                }

                                // do we have a next command?
                                if(i < pCount-1) {
                                    if(op=="<")
                                    {
                                            close(newPipe_fd[1]);
                                            dup2(newPipe_fd[0], 0);
                                            close(newPipe_fd[0]);
                                            int fildes = open(fileLoc.c_str(), O_RDWR|O_CREAT|O_APPEND, 0666);

                                            if (fildes == -1) { /* bad file name */ perror(fileLoc.c_str()); exit(1);}
                                            dup2(fildes,0);   /* copy fildes to 0 */
                                            close(fildes);    /* conserve file descriptors by closing excess one */
                                    }

                                    else if(op==">")
                                    {
                                            close(newPipe_fd[0]);
                                            dup2(newPipe_fd[1], 1);
                                            close(newPipe_fd[1]);
                                            int fildes = open(fileLoc.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666);
                                            if (fildes == -1) { /* bad file name */ perror("demo"); exit(1);}
                                            dup2(fildes,1);   /* copy fildes to 1 */
                                            close(fildes);    /* conserve file descriptors by closing excess one */

                                    }
                                    else if(op.compare("2>")==0)
                                    {
                                            close(newPipe_fd[0]);
                                            dup2(newPipe_fd[1], 1);
                                            close(newPipe_fd[1]);
                                            int fildes = open(fileLoc.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666);
                                            if (fildes == -1) { /* bad file name */ perror("demo"); exit(1);}
                                            dup2(fildes,2);   /* copy fildes to 1 */
                                            close(fildes);    /* conserve file descriptors by closing excess one */

                                    }
                                    else
                                    {
                                        close(newPipe_fd[0]);
                                        dup2(newPipe_fd[1], 1);
                                        close(newPipe_fd[1]);
                                    }
                                }

                                // execute command...
                               exitStat = execvp(cmd, arguments);
                                _exit(0);
                        } else {

                            if(!processQ.empty()&&(processQ.front().compare("||")==0||processQ.front().compare("&&")==0
                                        ))
                        {
                                wait(NULL);
                            }
                            else{


                                // do we have a previous command?

                                if(i > 0) {
                                        close(oldPipe_fd[0]);
                                        close(oldPipe_fd[1]);
                                }

                                // do we have a next command?
                                if(i < pCount-1) {
                                        oldPipe_fd[0] = newPipe_fd[0];
                                        oldPipe_fd[1] = newPipe_fd[1];
                                }

                                // wait for last command process?
                                if(i == pCount-1) {
                                    /*********Chris Edit 4/25/11**********************************************************************/
                                    FILE *fp;

                                    int rejcount = 0;
                                    int cpucount = 0;

                                    stringstream out;
                                    out << pid;
                                    string pidstr = out.str();
                                    string procfile;
                                    string needed = "VmSize";
                                    string kb = "kB";
                                    size_t find, find2;


                                    while(kill(pid, 0) == 0)				//checks if the child pid is still running then will == 0
                                    {

                                       int memorysize;
                                       char buf [200];
                                            string test1 = "cat /proc/";		//forming complete string with pid to get status
                                            test1.append(pidstr);
                                            test1.append("/status");
                                            fp = popen(&test1[0], "r");		//gets /proc/pid/status file
                                            while(fgets(buf, sizeof(buf)-1, fp))
                                            {
                                                procfile.append(buf);

                                            }

                                                    find = procfile.find(needed);
                                                    if(find != string::npos)		//extract VmSize from proc file
                                                    {
                                                            find = find+8;
                                                            find2 = procfile.find_last_of(" ", find+1);
                                                            string help = procfile.substr(find, find2);
                                                            memorysize = atoi(help.c_str());	//converts VmSize string to integer

                                                    }
                                            long int fiveth= 5000;
                                            int cputotal = cpuper(pid);
                                            double cpupercent = (cputotal/50);

                                            sleep(1);

                                            if(mblimit < memorysize)		//see if the used mem size is over the max memory
                                            {
                                                    if(rejcount == 0)
                                                    {
                                                            gettimeofday(&startTime, NULL);
                                                            rejcount++;
                                                    }
                                                    else{
                                                            gettimeofday(&endTime, NULL);
                                                            rejcount++;
                                                            int childiff = timediff();
                                                            if(childiff > mbtimer)
                                                            {
                                                                    kill(pid, 1);
                                                                    cout<<endl<<"PID: "<<pid<<" Killed due to exceeding "<<mblimit/1024<<" MB for "<<mbtimer<<"seconds"<<endl;
                                                                    rejcount = 0;

                                                            }
                                                    }
                                                   // cout<<endl<<"Memax:"<<memax<<" Memorysize:"<<memorysize;


                                            }
                                            if(cpupercent > cpulimit)
                                            {
                                                    if(cpucount == 0)
                                                    {
                                                            gettimeofday(&cpustart, NULL);
                                                            cpucount++;
                                                    }
                                                    else{
                                                            gettimeofday(&cpuend, NULL);
                                                            cpucount++;
                                                            int timediffs = timediff2();
                                                            if(timediffs > cputimer)
                                                            {
                                                                    kill(pid, 1);
                                                                    cout<<endl<<"PID: "<<pid<<" Killed due to exceeding "<<cpulimit<<"% for "<<cputimer<<" seconds"<<endl;
                                                                    cpucount = 0;
                                                            }
                                                    }
                                            }


                                                    procfile.clear();
                                                    wait(NULL);
                                    }
                                }
                                wait(NULL);

                       }
                        }
                       if(op.compare(">")==0||op.compare("<")==0||op.compare("2>")==0)
                                pCount--;
                       else if(op.compare("!")==0)
                            {
                                if(exitStat>0) exitStat = -1;
                                else exitStat = 1;
                            }

                        //*******************************************
                    //deallocate the dynamic char* array
                    delete [] arguments;
                }
        pCount = 0;
	return exitStat;
}

/******************************************/


/***********BUILT-IN COMMANDS**************/
int checkBuiltInCommands(){
        //initjobs(jobslist);
	if(!store.empty()){				// no CMD or ARG
		if(strcmp("exit", store[0].c_str()) == 0){	// exit safely from terminal
			exit(EXIT_SUCCESS);
		}

		if(strcmp("$?", store[0].c_str()) == 0){	// exit status
			Exit_Status();
			return 1;
		}
		if(strcmp("bg", store[0].c_str()) == 0){

			if(store[1].c_str() == NULL){
				return 0;
                        }
			else {
                            for(int i =0; i < store.size()-1; i++)
                                { store[i] = store[i+1]; }

			 	if(!store.empty())										// for commands, not configs
                 		 {
                   		 processQ_rest(pCount+1);
                   		 bool b = processQ.empty();
                    		int a = pCount;

                   		 int i =  launchjob(store[0].c_str(), BG);

                   		 i = (i <= 0)? 0:1;
                   		exit_status.push_back(i);
                     		}
					cout<<"\n[CookieMonster ] ";

			}
			return 1;
		}
		if(strcmp("fg", store[0].c_str()) == 0){
			if(store[1].c_str() == NULL)
				return 0;
			int jobID = (int) atoi(store[1].c_str());
			t_job * job = getjobjid(jobslist, jobID);
			if(job == NULL)
				return 0;
			if(job->state == SU || job->state == WI)
				putFG(job, TRUE);
			else
				putFG(job, FALSE);
			return 1;
		}

		if(strcmp("jobs", store[0].c_str()) == 0){
			printjobs(jobslist);
			return 1;
		}
		if(strcmp("kill", store[0].c_str()) == 0){
			if(store[1].c_str() == NULL)
				return 0;
			t_job *job = getjobjid(jobslist, atoi(store[1].c_str())) ;
			kill(job->pid, 1);
                        deletejob(job, job->pid);
			return 1;
		}
		if(strcmp("clear", store[0].c_str()) == 0){
			clearscreen();

		}
	}
	return 0;
}

/******************INITIALIZE***********************/
void init()
{
        CM_PID = getpid();
        CM_TERMINAL = STDIN_FILENO;
        CM_IS_INTERACTIVE = isatty(CM_TERMINAL);
        CM_PGID = getpgrp();

}

/**************LAUNCHJOB*******************/
int launchjob(string cmd, int state_){


int pC = pCount;
        int newPipe_fd[2];
        int oldPipe_fd[2];
        pid_t pid;
        //create argument array of character pointers
        string fileLoc;

        //exit status
	int exitStat;


                if(processQ.empty())
                    processQ_rest(pC);
                string tempCMD;


                for(int i = 0; i < pCount; i++) {

                        int index;
                        char** arguments = new char* [processQ.size()+1];
                        string op;
                        if(!processQ.empty()&&(processQ.front().compare("!")==0))
                        {
                            op = processQ.front();
                            processQ.pop();
                            pCount--;
                        }
                        if(!processQ.empty()&&(processQ.front().compare("||")==0||processQ.front().compare("&&")==0
                                        ))
                        {
                            op = processQ.front();
                            processQ.pop();
                            if(op.compare("&&")==0&&exitStat!=-1)
                            {
                                pCount=0;
                                break;
                            }
                            else if(op.compare("||")==0&&exitStat==-1)
                            {
                                pCount=0;
                                break;
                            }
                        }
                        tempCMD = processQ.front();
                        for(index = 0; !processQ.empty(); index++)
                        {
                                if(processQ.empty()||processQ.front()=="|"||processQ.front()==">"||processQ.front()=="<"
                                        ||processQ.front().compare("||")==0||processQ.front().compare("&&")==0
                                        ||processQ.front().compare("!")==0) break;
                                arguments[index] = const_cast<char*> (processQ.front().c_str());
                                processQ.pop();
                        }
                        //appending null at the end
                        arguments[index] = '\0';
                        if(!processQ.empty()&&processQ.front()=="|")
                         processQ.pop();

                        if(!processQ.empty()&&(processQ.front()==">"||processQ.front()=="<"))
                        {
                            op=processQ.front();
                            processQ.pop();
                            fileLoc = processQ.front();
                            processQ.pop();
                        }
                        const char* cmd;
                        for(int x = 0; x < PATH.size(); x++)
                        {
                            //temp string to store path, avoid altering PATH after each append
                            string temp = PATH[x];
                            temp.append(tempCMD);
                            if(FileExists(temp))
                            {
                                cmd = (temp).c_str();
                                break;
                            }
                        }

                        //fork and execute
                       if(i < pCount-1) {
                                pipe(newPipe_fd);
                        }

                        pid = fork();

                        if(pid == 0) {

		//////////////brigitte code //////////////////////////////////////
				//signal(SIGINT, &catch_interrupt;
				//signal(SIGQUIT, SIG_DFL);
				//signal(SIGTSTP, &catch_stop);
				//signal(SIGCHLD, &signalHandler_child);
				//signal(SIGTTIN, SIG_DFL);
				//usleep(20000);

				setpgrp();

				if(state_ == FG)
				{
				         signal(SIGTTOU, SIG_IGN);
					tcsetpgrp(CM_TERMINAL,getpid());
				}
				if(state_ == BG)
					cout << endl << "[" << (int)getpid() << "] " << endl;
			////////////////////////////////////////////////////////////////

                                alarm(timemax);
                                // do we have a previous command?
                                if(i > 0) {
                                        close(oldPipe_fd[1]);
                                        dup2(oldPipe_fd[0], 0);
                                        close(oldPipe_fd[0]);
                                }

                                // do we have a next command?
                                if(i < pCount-1) {
                                    if(op=="<")
                                    {
                                            int fildes = open(fileLoc.c_str(), O_RDWR|O_CREAT|O_APPEND, 0666);

                                            if (fildes == -1) { /* bad file name */ perror(fileLoc.c_str()); exit(1);}
                                            dup2(fildes,0);   /* copy fildes to 0 */
                                            close(fildes);    /* conserve file descriptors by closing excess one */
                                    }

                                    if(op==">")
                                    {
                                            int fildes = open(fileLoc.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666);
                                            if (fildes == -1) { /* bad file name */ perror("demo"); exit(1);}
                                            dup2(fildes,1);   /* copy fildes to 1 */
                                            close(fildes);    /* conserve file descriptors by closing excess one */

                                    }
                                    else
                                    {
                                        close(newPipe_fd[0]);
                                        dup2(newPipe_fd[1], 1);
                                        close(newPipe_fd[1]);
                                    }
                                }

                                // execute command...
                                exitStat = execvp(cmd, arguments);
                                exit(1);
                        } else {
				//////////////brigitte code////////////////////////
					setpgid(pid, pid);

					addjob(jobslist, pid, pid, state_, store[0]);

					t_job* job = getjobpid(jobslist, pid);

					if(state_ == FG)
					{
					    tcsetpgrp(CM_TERMINAL,pid);
						putFG(job, FALSE);

					}
					if(state_ == BG){
						putBG(job, FALSE);

					}
		/////////////////////////////////////////////////

                                // do we have a previous command?

                                if(i > 0) {
                                        close(oldPipe_fd[0]);
                                        close(oldPipe_fd[1]);
                                }

                                // do we have a next command?
                                if(i < pCount-1) {
                                        oldPipe_fd[0] = newPipe_fd[0];
                                        oldPipe_fd[1] = newPipe_fd[1];
                                }

                                // wait for last command process?


                       }

                       if(op.compare(">")==0)
                                pCount--;
                       else if(op.compare("<")==0)
                                pCount--;
                       else if(op.compare("!")==0)
                            {
                                if(exitStat==-1) exitStat = 1;
                                else exitStat = -1;
                            }

                        //*******************************************
                    //deallocate the dynamic char* array
                    delete [] arguments;
                }
        pCount = 0;
	return exitStat;




}

/*************PutForeground******************/
void putFG(t_job * job, int continueJob){
	job->state = FG;

	tcsetpgrp(CM_TERMINAL, job->pgid);
	if(continueJob){
		if(kill(-job->pgid, SIGCONT) < 0)
			perror("kill (SIGCONT)");
	}

	waitJob(job);
	signal(SIGTTOU, SIG_IGN);
	tcsetpgrp(CM_TERMINAL, CM_PGID);
}

/*************PutBackground******************/
void putBG(t_job* job, int continueJob){
	if(job == NULL)
		return;

	if(continueJob && job->state != WI)
		job->state = WI;
	if(continueJob)
		if(kill (-job->pgid, SIGCONT) < 0)
			perror("kill (SIGCONT)");
	signal(SIGTTOU, SIG_IGN);
	tcsetpgrp(CM_TERMINAL, CM_PGID);

}

/*****************WAIT_JOB*******************/
void waitJob(t_job* job)
{
    cout<<endl<<"I'm in waitjob";
        int terminationStatus;
        while (waitpid(job->pid, &terminationStatus, WNOHANG) == 0) {
                if (job->state == SU)
                        return;
        }
	deletejob(jobslist, job->pid);
}


/*************** KILL_JOB ******************/
void killJob(int JobID){
	t_job *job = getjobjid(jobslist, JobID) ;
	kill(job->pid, SIGKILL);
}

/**************EXIT_STATUS*****************/
void Exit_Status(){
	int index = 0;
	index = atoi(store[1].c_str());
	if(!exit_status.empty()){
		if(!(index > exit_status.size())){
			cout << "\nCommand " << index << ": ";
			cout << exit_status[index - 1] << endl;
                }
		else{
			cout << "\nCOMMANDS EXCEEDED.\t";
			cout << "[" << exit_status.size() << "]\n";
		}
	}
	else
		cout << "\nNO COMMANDS EXECUTED.\n";

	store.clear();
}

/***********************************/

void signalHandler_child(int p)
{
      pid_t pid;
        int terminationStatus;
        pid = waitpid(WAIT_ANY, &terminationStatus, WUNTRACED | WNOHANG);
        if (pid > 0) {

		t_job* job = getjobpid(jobslist, pid);

                if (job == NULL)
                        return;
                if (WIFEXITED(terminationStatus)) {
                        if (job->state == BG) {
                            cout << "[" << job->jid << "]+ Done\t   " << job->cmd << endl;
                            deletejob(jobslist, job->pid);
				signal(SIGTTOU, SIG_IGN);
                                tcsetpgrp(CM_TERMINAL, CM_PGID);
                        }
                } else if (WIFSIGNALED(terminationStatus)) {
                        cout << "[" << job->jid << "]+ KILLED\t   " << job->cmd << endl;
                        deletejob(jobslist, job->pid);
				signal(SIGTTOU, SIG_IGN);
                                tcsetpgrp(CM_TERMINAL, CM_PGID);
                } else if (WIFSTOPPED(terminationStatus)) {
                        if (job->state == BG) {
				signal(SIGTTOU, SIG_IGN);
                                tcsetpgrp(CM_TERMINAL, CM_PGID);
                                changeJobStatus(jobslist, pid, WI);
                                cout << "[" << numJobs << "]+   suspended\t   " << job->cmd<< endl;
                        } else {
                                tcsetpgrp(CM_TERMINAL, job->pgid);
                                changeJobStatus(jobslist, pid, SU);
                                cout << "[" << numJobs << "]+   stopped\t   " << job->cmd<< endl;
                        }
                        return;
                } else {
                        if (job->state == BG) {
                                deletejob(jobslist, job->pid);
                        }
                }
		signal(SIGTTOU, SIG_IGN);
                tcsetpgrp(CM_TERMINAL, CM_PGID);
        }
}
/***********************************/

/***********************************************
 * Helper routines that manipulate the job list
 **********************************************/
/* clearjob - Clear the entries in a job struct */
void clearjob( t_job *job) {
    job->pid = 0;
    job->jid = 0;
    job->pgid = 0;
    job->state = UNDEF;
    job->cmd.clear();
}

/* initjobs - Initialize the job list */
void initjobs( t_job *jobs) {
    int i;
    for (i = 0; i < MAXJOBS; i++)
	clearjob(&jobs[i]);
}

/* addjob - Add a job to the job list */
void addjob( t_job *jobs, pid_t pid, pid_t pgid, int state, string cmdline)
{
    int i, one;
    one = 0;

    if (pid < 1)
	return;

    for (i = 0; i < MAXJOBS; i++) {
	if (jobs[i].pid == 0 && !one) {
            one++;
	    jobs[i].pid = pid;
	    jobs[i].pgid = pgid;
	    jobs[i].state = state;
	    jobs[i].jid = ++nextjid;
	    if (nextjid > MAXJOBS)
		nextjid = 1;

            jobs[i].cmd.assign(cmdline);
	    numJobs++;
	}
    }

}

/*changeJobStatus - changes the status of the job whose PID=pid from the job list*/
int changeJobStatus( t_job *jobs, int pid, int state){
	int i;

	if (pid < 1)
		return 0;

	for (i = 0; i < MAXJOBS; i++) {
		if (jobs[i].pid == pid) {
			jobs[i].state == state;
	    		return 1;
		}
    	}
}


/* deletejob - Delete a job whose PID=pid from the job list */
int deletejob( t_job *jobs, pid_t pid)
{
    int i;

    if (pid < 1)
	return 0;

    for (i = 0; i < MAXJOBS; i++) {
	if (jobs[i].pid == pid) {
	    clearjob(&jobs[i]);
	    numJobs--;
	    return 1;
	}
    }
    return 0;
}
/* fgpid - Return PID of current foreground job, 0 if no such job */
pid_t fgpid( t_job *jobs) {
    int i;

    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].state == FG)
	    return jobs[i].pid;
    return 0;
}

/* getjobpid  - Find a job (by PID) on the job list */
 t_job *getjobpid( t_job *jobs, pid_t pid) {
    int i;

    if (pid < 1)
	return NULL;
    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].pid == pid)
	    return &jobs[i];
    return NULL;
}

/* getjobjid  - Find a job (by JID) on the job list */
 t_job *getjobjid( t_job *jobs, int jid)
{
    int i;

    if (jid < 1)
	return NULL;
    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].jid == jid)
	    return &jobs[i];
    return NULL;
}

/* printjobs - Print the job list */
void printjobs( t_job *jobs)
{
    int i;


    for (i = 0; i < MAXJOBS; i++) {
	if (jobs[i].pid != 0) {
	    cout << "[" << jobs[i].jid << "] (" << jobs[i].pid << ") ";
	    switch (jobs[i].state) {
                case UNDEF:
                    printf("-------");
		case BG:
		    printf("Running \t\t");
		    break;
		case FG:
		    printf("Foreground \t\t");
		    break;
		case SU:
		    printf("Suspended \t\t");
		    break;
	    default:
		    cout << "jobs: Internal error: job[" << i << "].state = ";
		    cout << jobs[i].state;
	    }
	    cout << jobs[i].cmd << endl;
	}
    }
}

/******************************************/


void catch_stop(int sig_num){
    /*
     *  re-set the signal handler again to catch_int, for next time
     *  when Ctrl - Z is pressed the shell should simply go to the next line
     */
        cout << "\nSuspended\n";
        fflush(stdout);
	signal(SIGTSTP, SIG_DFL);
	signal(SIGTTOU, SIG_IGN);
	tcsetpgrp(CM_TERMINAL, CM_PGID);

}
void catch_interrupt(int sig_num){
    /*
     *  re-set the signal handler again to catch_int, for next time
     *  when Ctrl - C is pressed the shell should simply go to the next line
     */
        fflush(stdout);
        signal(SIGINT, SIG_DFL);

}





/****Search if file exists in data directory****/
bool datasearch(string file, int index)
{
	for(int i=0; i<DATA.size(); i++)
	{
		string temp = DATA[i];
		string fullpath = temp.append(file);
		cout<<"\n\n"<<fullpath;
		if(FileExists(fullpath) == true)
		{
			store[index] = fullpath;
			return true;
		}

	}
}
/**********************************************/






int main(int argc, char *argv[], char*envp[])   //argc is a integer parameter containing count of arguments
												//argv is where the arguments are stored
{
//       DATA.push_back("/home/chris/");

//	signal(SIGTSTP, &catch_stop);
//	signal(SIGINT, &catch_interrupt);




	string in;													// user-input string
	int fd;

	/***************CLEARS_SCREEN_ON_START*************/
	if(fork()==0)
	{
		execve("/usr/bin/clear", argv, envp);					// clears the screen on start
		exit(1);
	}
	else{
		wait(NULL);
	}
	/**************************************************/
	cout<<"\nWelcome to the CookieMonster Shell!\n";
	cout<<"Constructed by Christopher Liu, Troung Trinh Nguyen and Brigitte De Leon\n\n\n";
	cout<<"[CookieMonster ] ";								// initial welcome

	while(cin)
	{

		getline(cin, in);
		if(in.size() == 0)										//if there is no input, command prompt continues
		{
			cout<<"[CookieMonster ] ";
		}
		else{
		/**************PARSER*******************/
			parse(in, store, PATH, DATA,timemax,mblimit,mbtimer,cpulimit,cputimer);
		/***************************************/

		/***************PARSER_TESTER******************/
//		int p = store.size();
//		for(int i=0; i<store.size(); i++)
//		{
//			//find a argument that does not contain "-", meaning is a file and call datasearch
//			if((store[i].find("-",0) == std::string::npos) && i != 0)
//			{
//				datasearch(store[i], i);
//
//			}
//			cout<<endl<<i+1<<" argument: "<<store[i];
//		}
		/*********************************************/

                if (checkBuiltInCommands() == 0) {

                /******************EXECUTABLE_COMMANDS**************/
		//execl syntax if your trying to enter ls -l: execl("/bin/ls", "ls", "-l", NULL)

                if(!store.empty())										// for commands, not configs
               {
                processQ_rest(pCount+1);
                int i = exec();

                i = (i <= 0)? 0:1;
                exit_status.push_back(i);
                 }

			cout<<"\n[CookieMonster ] ";
		}
		}
                in.clear();
		store.clear();
	}
}
